#include "jprocess_p.h"

namespace JXQ
{

JProcessPrivate::JProcessPrivate(JProcess *q_ptr) :
	JAbstractIOPrivate(q_ptr)
{
	for(int i=0; i<3; i++)
		m_buffer[i].open(JBuffer::ReadWrite);
}

JString JProcessPrivate::read(int channel)
{
	if( q_func()->state() == JProcess::Running and
		check(JProcess::ReadOnly) == false )
		return JString();

	int64_t size = m_buffer[channel].bytesAvailable();

	auto buf = std::shared_ptr<char>(new char[size+1]);
	memset(buf.get(), 0, size+1);

	m_buffer[channel].read(buf.get(), size);
	return JString(buf.get());
}

/*------------------------------------------------------------------------------------------------*/

JProcess::JProcess() :
	JAbstractIO(new JProcessPrivate(this))
{

}

JProcess::~JProcess()
{
	J_D(JProcess)
	if( d->m_watcher != nullptr )
		delete d->m_watcher;
}

int JProcess::waitFinished(int msecs)
{
	J_D(JProcess)
	int returnNumber = 0;

	if( d->m_watcher != nullptr )
	{
		if( d->m_watcher.operator JProcessWatcher*()->wait(&returnNumber, msecs) == false )
			return WaitTimeOut;
	}
	return returnNumber;
}

static Result executeResult(JProcess &jp, int msecs, bool timeoutKill)
{
	Result res;
	res.ret = jp.waitFinished(msecs);
	res.stdOut = jp.readAll();
	res.stdError = jp.readStdError();

	if( res.ret == JProcess::WaitTimeOut )
	{
		if( timeoutKill )
			jp.kill();
		else
			jp.detach();
	}
	return res;
}

Result JProcess::execute(const JString &fileName, const JStringList &args,
						 int msecs, bool timeoutKill)
{
	JProcess tmp;
	if( tmp.start(fileName, args) > 0 )
		return executeResult(tmp, msecs, timeoutKill);
	return Result{ tmp.returnCode(), "", "" };
}

Result JProcess::execute(ProcessMain &&processMain, const JStringList &args,
						 int msecs, bool timeoutKill)
{
	JProcess tmp;
	if( tmp.start(std::forward<ProcessMain>(processMain), args) > 0 )
		return executeResult(tmp, msecs, timeoutKill);
	return Result{ tmp.returnCode(), "", "" };
}

State JProcess::state() const
{
	return handle() == 0? Finished : Running;
}

void JProcess::setReadChannel(Channel channel)
{
	d_func()->m_channel = channel;
}

Channel JProcess::readChannel()
{
	return d_func()->m_channel;
}

JString JProcess::readStdOut()
{
	setErrno(0);
	return d_func()->read(1);
}

JString JProcess::readStdError()
{
	setErrno(0);
	return d_func()->read(2);
}

int JProcess::returnCode() const
{
	return d_func()->m_result;
}

JHandle JProcess::handle() const
{
	auto watcher = d_func()->m_watcher.operator JProcessWatcher*();
	return watcher == nullptr? 0 : watcher->pid();
}

int64_t JProcess::bytesAvailable() const
{
	return d_func()->m_buffer[1].bytesAvailable();
}

int64_t JProcess::size() const
{
	return d_func()->m_buffer[1].size();
}

int64_t JProcess::virtualRead(int64_t bufSize, void *buffer)
{
	setErrno(0);
	J_D(JProcess)

	if( state() == Finished or d->check(ReadOnly) )
	{
		if( d->m_channel == StdOut )
			return d->m_buffer[1].read(buffer, bufSize);
		else
			return d->m_buffer[2].read(buffer, bufSize);
	}
	return -1;
}

} // namespace JXQ
